<?php
namespace PsiFramework\Classes\DAO;

import("Classes.Model.Object");
import("Classes.DAO.AbstractDAO");
import("Classes.Util.HTTP.Connection");

use \PsiFramework\Classes\Model\Object;
use \PsiFramework\Classes\DAO\AbstractDAO;
use \PsiFramework\Classes\Factory\DAOFactory;
use \PsiFramework\Classes\Util\Database\SqlQuery;
use \PsiFramework\Classes\Util\Database\Builder\QueryBuilder;
use \PsiFramework\Classes\Util\Database\Query\SqlColumn;
use \PsiFramework\Classes\Util\Database\Query\SqlOperation;
use \PsiFramework\Classes\Util\Database\Query\SqlTable;
use \PsiFramework\Classes\Util\Database\Query\SqlValue;
use \PsiFramework\Classes\Util\Object\Date;
use \PsiFramework\Classes\Util\HTTP\Connection;

/**
 * PsiFramework
 * Php Simple Framework
 *
 * @author Rémi San
 * @version beta
 */

/**
 * The ConnectionDAO class is the Data Access Object for the Connections
 *
 * @package DAO
 */
class ConnectionDAO extends AbstractDAO
{

    protected static $_singleton = null;

    /**
     * Retrieve the connection through the specified session id
     *
     * @param    string        $sessionId
     * @return    Connection    the retrieved connection
     */
    public function findConnectionBySession(/*string*/ $sessionId)
    {

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createSelectQuery();

        $table = new SqlTable("connected", "psiframework");
        $queryBuilder->setTable($table);

        $whereClause = new SqlOperation(SqlOperation::$equal);
        $whereClause->addParameter(new SqlColumn("session_id", $table));
        $whereClause->addParameter(new SqlValue($sessionId));

        $queryBuilder->addWhereClause($whereClause);

        $query = $queryBuilder->buildQuery();
        $result = $this->_database->executeQuery($query);

        $connection = null ;
        if ($result) {
            $obj = $this->_database->getNextResult($result);
            if ($obj) {
                $connection = ConnectionDAO::getConnectionFromObj($obj);
            }
        }
        return $connection;
    }

    /**
     * Saves a connection
     *
     * @param    Connection    $connection    the connection to save
     * @return    bool        success ?
     */
    public function save(
        Object   &$connection,
        /*bool*/ $performTransaction=true,
        /*bool*/ $checkSecurity=true
    )
    {
        $result = null ;
        $return = false;

        if ($performTransaction) {
            $this->_database->startTransaction();
        }

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createInsertQuery();

        $table = new SqlTable("connected", "psiframework");
        $queryBuilder->setTable($table);

        $queryBuilder->addColumnValue(
            new SqlColumn("session_id", $table),
            new SqlValue($connection->getSessionId())
        );
        $queryBuilder->addColumnValue(
            new SqlColumn("user_id", $table),
            new SqlValue($connection->getUser()->getId())
        );
        $queryBuilder->addColumnValue(
            new SqlColumn("ip_address", $table),
            new SqlValue($connection->getIpAddress())
        );
        $queryBuilder->addColumnValue(
            new SqlColumn("last_seen", $table),
            new SqlValue($connection->getLastSeen()->getDateTime())
        );

        $query = $queryBuilder->buildQuery();
        $result = $this->_database->executeQuery($query);

        if ($performTransaction) {
            if ($result) {
                $this->_database->commit();
            } else {
                $this->_database->rollback();
            }
        }

        if ($result) {
            $return = true ;
        }

        return $return;
    }

    /**
     * Deletes a connection
     *
     * @param    Connection    $connection    the connection to delete
     * @return    bool        success ?
     */
    public function delete(
        Object     &$connection,
        /*bool*/   $performTransaction=true,
        /*bool*/   $checkSecurity=true
    )
    {
        $result = null ;
        $return = false;

        if ($performTransaction) {
            $this->_database->startTransaction();
        }

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createDeleteQuery();

        $table = new SqlTable("connected", "psiframework");
        $queryBuilder->setTable($table);

        $whereClause = new SqlOperation(SqlOperation::$equal);
        $whereClause->addParameter(new SqlColumn("session_id", $table));
        $whereClause->addParameter(new SqlValue($connection->getSessionId()));

        $queryBuilder->addWhereClause($whereClause);

        $query = $queryBuilder->buildQuery();
        $result = $this->_database->executeQuery($query);

        if ($performTransaction) {
            if ($result) {
                $this->_database->commit();
            }
            else $this->_database->rollback();
        }
        if ($result) $return = true ;

        return $return;
    }

    /**
     * Updates a connection
     *
     * @param    Connection    $connection    the connection to update
     * @return    bool        success ?
     */
    public function update(Object $connection)
    {
        $result = null ;
        $return = false;

        $this->_database->startTransaction();
        $now = Date::now();

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createUpdateQuery();

        $table = new SqlTable("connected", "psiframework");
        $queryBuilder->setTable($table);

        $queryBuilder->addColumnValue(
            new SqlColumn("last_seen", $table),
            new SqlValue($now->getDateTime())
        );

        $whereClause = new SqlOperation(SqlOperation::$equal);
        $whereClause->addParameter(new SqlColumn("session_id", $table));
        $whereClause->addParameter(new SqlValue($connection->getSessionId()));

        $queryBuilder->addWhereClause($whereClause);

        $query = $queryBuilder->buildQuery();
        $result = $this->_database->executeQuery($query);

        if ($result) {
            $return = true ;
            $this->_database->commit();
        } else {
            $this->_database->rollback();
        }

        return $return;
    }

    /**
     * Deletes the old connections
     *
     * @param     int        $timeout
     * @return     bool    did the delete work ?
     */
    public function deleteOldConnections(/*int*/ $timeout)
    {
        $limitTime = Date::time(time()-$timeout);
        $result = null ;
        $return = false;

        $this->_database->startTransaction();
        $now = Date::now();

        $queryBuilder = new QueryBuilder();
        $queryBuilder->createDeleteQuery();

        $table = new SqlTable("connected", "psiframework");
        $queryBuilder->setTable($table);

        $whereClause = new SqlOperation(SqlOperation::$lowerThan);
        $whereClause->addParameter(new SqlColumn("last_seen", $table));
        $whereClause->addParameter(new SqlValue($limitTime->getDateTime()));

        $queryBuilder->addWhereClause($whereClause);

        $query = $queryBuilder->buildQuery();
        $result = $this->_database->executeQuery($query);

        if ($result) {
            $return = true ;
            $this->_database->commit();
        }
        else $this->_database->rollback();

        return $return;
    }

    /**
     * Returns a Connection from an sql object
     *
     * @param     SQLObject    $obj    the SQL object
     * @return     Connection    the connection to retrieve from the SQL object
     */
    public static function getConnectionFromObj($obj)
    {
        $connection = null ;
        if ($obj->{'user_id'} != null) {
            $userDao = DAOFactory::getDAO("User");
            $user = $userDao->retrieveByPk($obj->{'user_id'}, false, false);

            if ($user != null
                && $obj->{'session_id'} != null
                && $obj->{'ip_address'} != null
                && $obj->{'last_seen'} != null
            ) {

                $connection = new Connection(
                    $user,
                    $obj->{'session_id'},
                    $obj->{'ip_address'},
                    Date::getDateFromString($obj->{'last_seen'})
                );

            }
        }
        return $connection ;
    }

    /**
     * Function to be called instead of the constructor.
     *
     * @return ConnectionDAO instance
     */
    public static function getInstance()
    {

        if (self::$_singleton==null) {
            self::$_singleton = new ConnectionDAO();
        }

        return self::$_singleton;
    }

}
